//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Web.Configuration;
using System.IO;
using WCSFContrib.PageFlow.Xml.Configuration;
using System.Globalization;
using System.Reflection;
using WCSFContrib.PageFlow.Xml.Properties;

namespace WCSFContrib.PageFlow.Xml.Services
{
    public class WebConfigStore
    {
        private string _baseDirectory;

        public WebConfigStore()
            : this(String.Empty)
        {
        }

        public WebConfigStore(string baseDirectory)
        {
            _baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, baseDirectory);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public IDictionary<string, NavigationGraph> GetXmlPageFlowNavigationGraphs()
        {
            XmlPageFlowConfigurationSection configSection = GetConfiguration(Path.Combine(_baseDirectory, "Web.Config"));

            return PopulateSection(configSection); 
        }

        private IDictionary<string, NavigationGraph> PopulateSection(XmlPageFlowConfigurationSection configSection)
        {
            IDictionary<string, View> temporalViews = new Dictionary<string, View>(configSection.Views.Count);
            IDictionary<string, NavigationGraph> navigationGraphs = new Dictionary<string, NavigationGraph>(new EqualityLowerComparer<string>());
                        
            foreach (ViewConfigurationElement configView in configSection.Views)
            {
                View view = new View(configView.Name, configView.Type);

                temporalViews.Add(view.Name, view);
            }

            foreach (NavigationGraphConfigurationElement configNavGraph in configSection.NavigationGraphs)
            {
                IXmlPageFlowInstanceStore xmlPageFlowInstanceStore = GetInstanceStore(configNavGraph.StatePersist, configSection.ObjectTypes.StatePersistenceCollection);
                Type xmlPageFlowType = GetPageFlowType(configNavGraph.PageFlowType, configSection.ObjectTypes.PageFlowTypesCollection);

                NavigationGraph navigationGraph = new NavigationGraph(configNavGraph.Name,
                                                       xmlPageFlowInstanceStore,
                                                       temporalViews[configNavGraph.StartView],
                                                       configNavGraph.AbortUrl,
                                                       configNavGraph.Abandonable,
                                                       configNavGraph.NotRunningUrl,
                                                       xmlPageFlowType);

                navigationGraph.Constrained = configSection.AllowBackButton;

                foreach (NodeConfigurationElementCollection configNode in configNavGraph)
                {
                    View view = temporalViews[configNode.View];

                    if (view != null && !navigationGraph.Views.Contains(view))
                        navigationGraph.Views.Add(view);

                    foreach (NavigateToConfigurationElement configNavigateTo in configNode)
                    {
                        if (String.IsNullOrEmpty(configNavigateTo.View) && String.IsNullOrEmpty(configNavigateTo.Condition))
                            throw new ConfigurationErrorsException(String.Format(CultureInfo.CurrentCulture, Resources.ConfigurationInvalidTransition, configNavigateTo.NavigateValue));

                        View transitionView = null;

                        if (temporalViews.ContainsKey(configNavigateTo.View))
                            transitionView = temporalViews[configNavigateTo.View];

                        string conditionName = (String.IsNullOrEmpty(configNavigateTo.Condition)) ? null : configNavigateTo.Condition;

                        view.Transitions.Add(new Transition(configNavigateTo.NavigateValue, transitionView, conditionName));

                        if (transitionView != null && !navigationGraph.Views.Contains(transitionView))
                            navigationGraph.Views.Add(transitionView);
                    }
                }

                navigationGraphs.Add(navigationGraph.PageFlowType.AssemblyQualifiedName, navigationGraph);
            }

            return navigationGraphs;
        }

        private static XmlPageFlowConfigurationSection GetConfiguration(string configFilePath)
        {
            XmlPageFlowConfigurationSection configuration;
            System.Web.HttpContext context = System.Web.HttpContext.Current;

            if (context == null)
            {
                configuration = (XmlPageFlowConfigurationSection)GetConfigurationForCustomFile(configFilePath).GetSection("pageFlow/xmlPageFlow");
            }
            else
            {
                configuration = (XmlPageFlowConfigurationSection)ConfigurationManager.GetSection("pageFlow/xmlPageFlow");
            }

            return configuration;
        }

        private static System.Configuration.Configuration GetConfigurationForCustomFile(string fileName)
        {
            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            fileMap.ExeConfigFilename = fileName;
            File.SetAttributes(fileMap.ExeConfigFilename, FileAttributes.Normal);
            return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        }


        private IDictionary<string, IXmlPageFlowInstanceStore> instanceStores = new Dictionary<string, IXmlPageFlowInstanceStore>();
        /// <summary>
        /// Lazy load the persistence stores as navigation graphs that uses them appear in the configuration
        /// </summary>
        private IXmlPageFlowInstanceStore GetInstanceStore(string name, Dictionary<string, StatePersistenceProviderSettings> settings)
        {
            if (instanceStores.ContainsKey(name))
                return instanceStores[name];

            StatePersistenceProviderSettings storeSettings = settings[name];
            if (storeSettings.Type == null)
                throw new ConfigurationErrorsException(String.Format(CultureInfo.CurrentCulture, Resources.InvalidConfigurationTypeValue, storeSettings.Name));

            IXmlPageFlowInstanceStore store;
            try
            {
                store = (IXmlPageFlowInstanceStore)Activator.CreateInstance(storeSettings.Type, storeSettings.AdditionalAttributes);
            }
            catch (MissingMethodException)
            {
                store = (IXmlPageFlowInstanceStore)Activator.CreateInstance(storeSettings.Type);
            }
            instanceStores.Add(storeSettings.Name, store);

            return store;
        }

        private static Type GetPageFlowType(string name, Dictionary<string, PageFlowTypeSettings> settings)
        {
            PageFlowTypeSettings pageFlowTypeSettings = settings[name];
            
            if (pageFlowTypeSettings.Type == null)
                throw new ConfigurationErrorsException(String.Format(CultureInfo.CurrentCulture, Resources.InvalidConfigurationPageFlowTypeValue, pageFlowTypeSettings.Name));

            return pageFlowTypeSettings.Type;
        }
    }
}
